<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>drag</title>
  <style>
      body {
        margin: 0;
        overflow: hidden;
      }
      canvas {
        display: block;
        background: blue;
      }
      .op-btn {
        position: absolute;
        z-index: 3;
        top: 10px;
        left: 10px;
      }
  </style>
</head>
<body>
<button class="op-btn" onclick="onClickAdd()">Add</button>
<canvas id="mycanvas"></canvas>
<script src="../lib/pixi.js"></script>
<script type="text/javascript">
  window.oncontextmenu=function(e){
    //取消默认的浏览器自带右键 很重要！！
    e.preventDefault();
  }

  function createGradTexture(color = 'rgba(255, 0, 0, 1.0)') {
    const endColor = color.substring(0, color.lastIndexOf(',')) + ', 0.0)';
    const quality = 256;
    const canvas = document.createElement('canvas');
    canvas.width = quality;
    canvas.height = quality;

    const ctx = canvas.getContext('2d');

    const cx = quality * 0.5;
    const cy = quality * 0.5;
    // use canvas2d API to create gradient
    const grd = ctx.createRadialGradient(cx, cy, 6, cx, cy, quality * 0.4);
    grd.addColorStop(0, color);
    // grd.addColorStop(0.7, 'rgba(255, 255, 255, 0.7)');
    grd.addColorStop(1, endColor);

    ctx.fillStyle = grd;
    ctx.fillRect(0, 0, quality, quality);

    return PIXI.Texture.from(canvas);
  }

  function createRectTexture(width, height) {
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = '#fff';
    ctx.fillRect(0, 0, width, height);
    return PIXI.Texture.from(canvas);
  }

  function createCircleTexture(radius) {
    const canvas = document.createElement('canvas');
    canvas.width = radius * 2;
    canvas.height = radius * 2;
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = '#fff';
    ctx.arc(radius, radius, radius, 0, Math.PI * 2);
    ctx.fill();
    return PIXI.Texture.from(canvas);
  }

  function createCloseBtnTexture(radius = 12, bgColor = '#0079fe') {
    const canvas = document.createElement('canvas');
    canvas.width = radius * 2;
    canvas.height = radius * 2;
    const ctx = canvas.getContext('2d');
    ctx.fillStyle = bgColor;
    ctx.arc(radius, radius, radius, 0, Math.PI * 2);
    ctx.fill();

    const r = radius;
    const w = r * 0.666;
    const h = r * 0.666;
    const d = r * 0.43;
    let p1 = {x: r-w, y: r-h};
    let p2 = {x: r-w+d, y: r-h};
    let p3 = {x: r+w, y: r+h};
    let p4 = {x: r+w-d, y: r+h};
    ctx.beginPath();
    ctx.moveTo(p1.x, p1.y);
    ctx.lineTo(p2.x, p2.y);
    ctx.lineTo(p3.x, p3.y);
    ctx.lineTo(p4.x, p4.y);
    ctx.closePath();
    ctx.fillStyle = '#fff';
    ctx.fill();
    [p1, p2, p3, p4].forEach(it => it.x = r * 2 - it.x);
    ctx.beginPath();
    ctx.moveTo(p1.x, p1.y);
    ctx.lineTo(p2.x, p2.y);
    ctx.lineTo(p3.x, p3.y);
    ctx.lineTo(p4.x, p4.y);
    ctx.closePath();
    ctx.fillStyle = '#fff';
    ctx.fill();

    return PIXI.Texture.from(canvas);
  }

  const gradRed = createGradTexture();
  const gradGreen = createGradTexture('rgba(12, 200, 56, 1.0)');
  const marks = [];
  const canvas = document.getElementById('mycanvas')
  const app = new PIXI.Application({
    view: canvas,
    width: window.innerWidth,
    height: window.innerHeight
  })

  const texture = PIXI.Texture.from('../assets/grid1.jpg')
  const img = new PIXI.Sprite(texture)
  img.x = app.renderer.width / 2
  img.y = app.renderer.height / 2
  img.anchor.x = 0.5
  img.anchor.y = 0.5
  img.interactive = true
  app.stage.addChild(img)

  //
  const bgBox = new PIXI.Graphics();
  // draw a rounded rectangle
  bgBox.lineStyle(2, 0x0079fe, 1);
  bgBox.beginFill(0x656565, 0.0);
  const w = 172;
  const h = 334;
  bgBox.drawRoundedRect(0, 0, w, h, 8);
  bgBox.endFill();
  bgBox.position.set(100, 100)
  app.stage.addChild(bgBox);

  const tw = 56;
  const th = 16;
  bgBox.beginFill(0x0079fe);
  bgBox.drawRect((w - tw) * 0.5, 0, 56, 10);
  bgBox.endFill();

  const closeBtn = new PIXI.Sprite(createCloseBtnTexture());
  closeBtn.anchor.set(0.5, 0.5)
  bgBox.addChild(closeBtn)

  const txtPaddingLeft = 24
  const unitWidth = 48;
  const labelWidth = 56;
  const btnPadding = 16
  const btnTxtStyle = new PIXI.TextStyle({
    fontSize: 14,
    fontWeight: '200',
    fill: ['#000000'],
    fontFamily: 'Arial Normal',
  })
  const titleTxtStyle = new PIXI.TextStyle({
    // fontFamily: 'Arial',
    fontSize: 16,
    fontWeight: 'bold',
    fill: ['#ffffff']
  })
  const txtStyle = new PIXI.TextStyle({
    fontSize: 14,
    fill: ['#ffffff']
  })
  const lineItems = [
    {isTitle: true, text: 'B3变压器（1AE5）'},
    {isCircle: true, color: '#ff0015', radius: 6, paddingLeft: txtPaddingLeft},
    {isInfo: true, text: 'Ucb:', unit: 'KV', value: '0.0', vc: '#ffcc15', vsx: labelWidth + 8},
    {isInfo: true, text: 'Ubc:', unit: 'KV', value: '0.0', vc: '#ffcc15', vsx: labelWidth + 8},
    {isInfo: true, text: 'Uac:', unit: 'KV', value: '0.0', vc: '#ff0015', vsx: labelWidth + 8},
    {isInfo: true, text: 'la:', unit: 'A', value: '0.0', vc: '#ffcc15', vsx: labelWidth + 8},
    {isInfo: true, text: 'lb:', unit: 'A', value: '0.0', vc: '#ffcc15', vsx: labelWidth + 8},
    {isInfo: true, text: 'lc:', unit: 'A', value: '0.0', vc: '#ff0015', vsx: labelWidth + 8},
    {isInfo: true, text: 'P:', unit: 'KW', value: '0.0', vc: '#ffffff', vsx: labelWidth + 8},
    {isInfo: true, text: 'EF:', unit: 'KWh', value: '0', vc: '#ffffff', vsx: labelWidth + 8},
    {isInfo: true, text: 'FF:', value: '0.0', vc: '#ffffff', vsx: labelWidth + 8},
    {isCircle: true, color: '#ff0015', radius: 6, paddingLeft: txtPaddingLeft},
    {isBtn: true, text: '开关断拉', btnColor: '#ff0015'},
    {isBtn: true, text: '键自己藕控', btnColor: '#95f204'},
    {isBtn: true, text: '藕断控置', btnColor: '#95f204'},
    // {isTitle: true, text: 'B2变压器温度'},
    // {isCircle: true, color: '#95f204', radius: 6, paddingLeft: txtPaddingLeft},
    // {isInfo: true, text: 'Ta(℃）', value: '46.2', vc: '#ffcc15', vsx: w*0.6},
    // {isInfo: true, text: 'Tb(℃）', value: '46.2', vc: '#95f204', vsx: w*0.6},
    // {isInfo: true, text: 'Tc(℃）', value: '52.8', vc: '#ff0015', vsx: w*0.6},
  ]

  let curHeight = th;
  lineItems.forEach(item => {
    if (item.isTitle) {
      const title = new PIXI.Text(item.text, titleTxtStyle)
      title.anchor.set(0.5, 0);
      title.position.set(w * 0.5, curHeight); // 居中对齐
      bgBox.addChild(title);
      curHeight += title.height;
    } else if (item.isCircle) {
      const circle = new PIXI.Sprite(createCircleTexture(6));
      circle.tint = parseInt(item.color.substring(1), 16)
      circle.position.set(item.paddingLeft, curHeight)
      bgBox.addChild(circle);
      curHeight += circle.height + 4;
    } else if (item.isInfo) {
      const txtLabel = new PIXI.Text(item.text, txtStyle)
      txtLabel.anchor.set(0.0, 0.0)
      txtLabel.position.set(txtPaddingLeft, curHeight)
      const vLabel = new PIXI.Text(item.value, txtStyle)
      vLabel.anchor.set(0, 0);
      vLabel.position.set(item.vsx, curHeight)
      vLabel.tint = parseInt(item.vc.substring(1), 16);
      bgBox.addChild(txtLabel, vLabel);
      if (item.unit) {
        const uLabel = new PIXI.Text(item.unit, txtStyle);
        uLabel.anchor.set(0, 0);
        uLabel.position.set(w - unitWidth, curHeight)
        bgBox.addChild(uLabel);
      }
      curHeight += txtLabel.height + 5
    } else if (item.isBtn) {
      const label = new PIXI.Text(item.text, btnTxtStyle);
      label.anchor.set(0.5, 0);
      label.position.set(w * 0.5, curHeight);
      const bgWidth = w - btnPadding * 2;
      const btnBg = new PIXI.Sprite(createRectTexture(bgWidth, label.height));
      btnBg.tint = parseInt(item.btnColor.substring(1), 16)
      btnBg.position.set(btnPadding, curHeight)
      bgBox.addChild(btnBg, label)
      curHeight += label.height + 8
    }
  })

  app.stage.interactive = true;
  app.stage.hitArea = app.screen;
  const mouseDownPos = {x: 0, y: 0};
  let imgDownPos = {x: 0, y: 0};
  const imgPos0 = {x: 0, y: 0};
  const markPos0 = {x: 0, y: 0};
  let dragging = false; // true 表示正在拖动背景图
  let draggingMark = null;
  let selectedMark = null;
  let adding = false;
  let originScale = 1;
  const markSize = 50;
  const pickSq = markSize * markSize * 0.25;
  app.stage.on('pointerdown', onPointerDown);
  app.stage.on('pointermove', onPointerMove);
  // app.stage.on('wheel', onMouseWheel);
  document.addEventListener('wheel', onMouseWheel);
  app.stage.on('pointerup', onPointerUp);
  app.stage.on('pointerupoutside', onPointerUp);

  function onClickAdd() {
    adding = true;
  }
  function onPointerDown(event) {
    console.log(event.data.global);
    mouseDownPos.x = event.data.global.x;
    mouseDownPos.y = event.data.global.y
    if (adding) {
      if (event.data.originalEvent.button === 2) {
        adding = false;
        return;
      }
      const texture = marks.length % 2 === 0 ? gradRed : gradGreen;
      const markSprite = new PIXI.Sprite(texture);
      markSprite.anchor.x = 0.5
      markSprite.anchor.y = 0.5
      markSprite.width = markSize;
      markSprite.height = markSize;
      originScale = markSprite.scale.x;
      // 世界坐标 转 本地坐标
      const imgLocalPos = img.worldTransform.applyInverse(mouseDownPos);
      markSprite.position.set(imgLocalPos.x, imgLocalPos.y);
      img.addChild(markSprite);
      marks.push(markSprite);
    } else {
      const {x, y} = img.worldTransform.applyInverse(mouseDownPos);
      imgDownPos.x = x;
      imgDownPos.y = y;
      let minDist = Infinity;
      let minIndex = -1;
      marks.forEach((mark, index) => {
        const px = mark.position.x;
        const py = mark.position.y;
        const distSq = (px - x) * (px - x) + (py - y) * (py - y);
        if (minDist > distSq && distSq <= pickSq * img.scale.x) {
          minDist = distSq;
          minIndex = index
        }
      });
      if (minIndex > -1) {
        draggingMark = marks[minIndex];
        markPos0.x = draggingMark.x;
        markPos0.y = draggingMark.y;
      } else {
        draggingMark = null;
        imgPos0.x = img.x;
        imgPos0.y = img.y;
        dragging = true;
      }
    }
  }

  function onPointerMove(event) {
    if (dragging) {
      const {x, y} = event.data.global;
      const dx = x - mouseDownPos.x;
      const dy = y - mouseDownPos.y;
      img.x = imgPos0.x + dx;
      img.y = imgPos0.y + dy;
    } else if (draggingMark) {
      const {x, y} = img.worldTransform.applyInverse(event.data.global);
      const dx = x - imgDownPos.x;
      const dy = y - imgDownPos.y;
      draggingMark.x = markPos0.x + dx;
      draggingMark.y = markPos0.y + dy;
    }
  }

  function onPointerUp(event) {
    dragging = false;
    selectedMark = draggingMark;
    draggingMark = null;
  }

  function onMouseWheel(event) {
    // console.log('onMouseWheel', event);
    zoom(event.clientX, event.clientY, event.deltaY < 0);
  }

  document.addEventListener('keydown', (e) => {
    if (e.code === 'Delete') {
      if (selectedMark) {
        img.removeChild(selectedMark);
        const index = marks.indexOf(selectedMark);
        if (index >= 0);
        marks.splice(index, 1);
      }
    }
  });

  const getGraphCoordinates = (function () {
    const ctx = {
      global: { x: 0, y: 0} // store it inside closure to avoid GC pressure
    };

    return function (x, y) {
      ctx.global.x = x; ctx.global.y = y;
      return PIXI.InteractionData.prototype.getLocalPosition.call(ctx, img);
    }
  }());

  function zoom(x, y, isZoomIn) {
    const direction = isZoomIn ? 1 : -1;
    const factor = (1 + direction * 0.1);
    img.scale.x *= factor;
    img.scale.y *= factor;

    // Technically code below is not required, but helps to zoom on mouse
    // cursor, instead center of graphGraphics coordinates
    // const beforeTransform = getGraphCoordinates(x, y);
    // img.updateTransform();
    // const afterTransform = getGraphCoordinates(x, y);

    const beforeTransform = img.worldTransform.applyInverse({x, y});
    img.updateTransform();
    const afterTransform = img.worldTransform.applyInverse({x, y});

    // const beforeTransform = img.localTransform.apply({x, y});
    // img.updateTransform();
    // const afterTransform = img.localTransform.apply({x, y});

    img.position.x += (afterTransform.x - beforeTransform.x) * img.scale.x;
    img.position.y += (afterTransform.y - beforeTransform.y) * img.scale.y;
    img.updateTransform();
  }

  // app.ticker.add(animate)
  // let time = 0.0;
  // function animate(delta) {
  //   const f = (0.7 + Math.sin(time) * 0.5) * originScale;
  //   marks.forEach(mark => {
  //     mark.scale.set(f, f);
  //   })
  //   time += delta / 36;
  // }
</script>
</body>
</html>
